package dao

import (
	"database/sql"
	"fmt"
	"game/game-db/orm"
	"game/game-db/orm/column"
	"game/game-db/orm/meta"
	"game/game-db/orm/metag"
	"game/game-db/util"
	"strings"

	"go.uber.org/zap"
)

var SERVER_IDENTITY int64 = 0

func BuildTable(entityMeta meta.IEntityMeta, db *sql.DB, tableName string) {
	if SERVER_IDENTITY <= 0 {
		panic("before build table, must setup SERVER_IDENTITY to positive")
	}

	table := &TableExist{}
	tableMeta := meta.NewMiniEntityMeta(table)
	tableDefined := QueryList(tableMeta, db, TableExistSql(tableName))

	if len(tableDefined) <= 0 {
		//创建表
		createTable(entityMeta, db, tableName)
	} else {
		//检查表结构
		updateTable(entityMeta, db, tableName)
	}
	//检查表主键与索引
	checkKeys(entityMeta, db, tableName)
	//TODO 自增ID的最大值设置
	updateAutoIncrement(entityMeta, db, tableName)
}

// 创建表的sql
func createTable(entityMeta meta.IEntityMeta, db *sql.DB, tableName string) {
	var builder strings.Builder
	builder.WriteString(fmt.Sprintf("create table `%v` (", tableName))
	for i, column := range entityMeta.Columns() {
		builder.WriteString(columnDefine(column))
		if i < len(entityMeta.Columns())-1 {
			builder.WriteString(",")
		}
	}
	builder.WriteString(fmt.Sprintf(") engine=InnoDB default charset=%s comment='%s';", entityMeta.TableCharset(), entityMeta.TableComment()))
	zap.L().Info("Create Table DDL", zap.String("SQL", builder.String()))
	db.Exec(builder.String())
}

func updateTable(entityMeta meta.IEntityMeta, db *sql.DB, tableName string) {
	columnsMeta := meta.NewMiniEntityMeta(&TableColumn{})
	columnsDefined := QueryList(columnsMeta, db, ShowColumnsSql(tableName))

	ddlSqls := make([]string, 0)

	//检查是否需要删除列与修改列
	for _, v := range columnsDefined {
		column := v.(*TableColumn)
		columnMeta := entityMeta.ColumnMeta(column.Field)
		if columnMeta == nil {
			//删除列
			ddlSqls = append(ddlSqls, DropColumn(tableName, column.Field))
		} else {
			needChange := columnMeta.IsChange(column.Type, column.Extra)
			if needChange {
				//修改列
				ddlSqls = append(ddlSqls, ModifyColumnSql(tableName, columnDefine(columnMeta)))
			}
		}
	}
	//检查是否需要新增列
	columnsMap := make(map[string]bool)
	for _, v := range columnsDefined {
		column := v.(*TableColumn)
		columnsMap[column.Field] = true
	}
	for _, column := range entityMeta.Columns() {
		_, ex := columnsMap[column.ColumnName()]
		if ex {
			continue
		}
		//新增列
		ddlSqls = append(ddlSqls, AddColumn(tableName, columnDefine(column)))
	}

	for _, ddlsql := range ddlSqls {
		fmt.Println("修改表", ddlsql)
		_, err := db.Exec(ddlsql)
		if err != nil {
			fmt.Println(err)
		}
	}
}

// 检查主键与索引
func checkKeys(entityMeta meta.IEntityMeta, db *sql.DB, tableName string) {
	ddlSqls := make([]string, 0)

	keysMeta := meta.NewMiniEntityMeta(&TableKey{})
	keysDefined := QueryList(keysMeta, db, KeysSql(tableName))

	//对查询出来的主键与索引进行分组
	keysGroup := make(map[string][]*TableKey)
	for _, v := range keysDefined {
		key := v.(*TableKey)
		value, ex := keysGroup[key.KeyName]
		if !ex {
			value = make([]*TableKey, 0)
		}
		value = append(value, key)
		keysGroup[key.KeyName] = value
	}

	//处理主键
	primaryKeys, ex := keysGroup["PRIMARY"]
	if !ex {
		//创建主键
		ddlSqls = append(ddlSqls, CreatePrimaryKeySql(entityMeta.PrimaryKey(), tableName))
	} else {
		if len(primaryKeys) != 1 {
			//丢掉重建
			ddlSqls = append(ddlSqls, DropPrimaryKeySql(tableName))
			for _, v := range primaryKeys {
				columnMeta := entityMeta.ColumnMeta(v.ColumnName)
				ddlSqls = append(ddlSqls, ModifyColumnSql(tableName, columnDefine(columnMeta)))
			}
			ddlSqls = append(ddlSqls, CreatePrimaryKeySql(entityMeta.PrimaryKey(), tableName))
		} else {
			if primaryKeys[0].ColumnName != entityMeta.PrimaryKey().ColumnName() {
				//丢掉重建
				ddlSqls = append(ddlSqls, DropPrimaryKeySql(tableName))
				columnMeta := entityMeta.ColumnMeta(primaryKeys[0].ColumnName)
				ddlSqls = append(ddlSqls, ModifyColumnSql(tableName, columnDefine(columnMeta)))
				ddlSqls = append(ddlSqls, CreatePrimaryKeySql(entityMeta.PrimaryKey(), tableName))
			}
		}
	}
	//处理索引
	for _, indexDefine := range entityMeta.Indexes() {
		indexName := "idx"
		columns := make([]string, 0)
		for _, fieldName := range indexDefine.Fields {
			columnName := entityMeta.ColumnNameByFieldName(fieldName)
			indexName += "_" + columnName
			columns = append(columns, columnName)
		}
		dbIndexes, ex := keysGroup[indexName]
		if ex {
			//查索引的定义是否与类中的定义一致
			if len(dbIndexes) != len(indexDefine.Fields) {
				//修改索引
				ddlSqls = append(ddlSqls, ModifyIndexSql(tableName, string(indexDefine.IndexType), indexName, columns...))
			} else {
				same := true
				for i := range columns {
					if columns[i] != dbIndexes[i].ColumnName {
						same = false
						break
					}
				}
				if !same {
					//修改索引
					ddlSqls = append(ddlSqls, ModifyIndexSql(tableName, string(indexDefine.IndexType), indexName, columns...))
				}
			}
		} else {
			//新增索引
			ddlSqls = append(ddlSqls, AddIndexSql(tableName, string(indexDefine.IndexType), indexName, columns...))
		}
	}
	//如果数据库表中存在与类定义中未定义的索引时，不进行删除
	for _, ddlsql := range ddlSqls {
		fmt.Println("检查主键与索引", ddlsql)
		_, err := db.Exec(ddlsql)
		if err != nil {
			fmt.Println(err)
		}
	}
}

// 列的定义
func columnDefine(columnMeta column.IColumnMeta) string {
	buffer := util.NewStringBuffer()
	buffer.Appendf("`%s` %s %s comment '%s'", columnMeta.ColumnName(), columnMeta.DbColumnType(), columnMeta.DefaultValue(), columnMeta.Comment())
	return buffer.ToString()
}

func updateAutoIncrement(entityMeta meta.IEntityMeta, db *sql.DB, tableName string) {
	primaryKey := entityMeta.PrimaryKey()
	if primaryKey.KeyStrategy() != metag.AUTO && primaryKey.KeyStrategy() != metag.AUTOWIRE {
		return
	}

	versionMeta := meta.NewMiniEntityMeta(&MysqlVersion{})
	versionDefined := Query(versionMeta, db, MysqlVersionSql).(*MysqlVersion)

	//当前的自增主键
	var currAutoId int64
	tableAutoIncrementMeta := meta.NewMiniEntityMeta(&TableAutoIncrement{})
	var tableAutoincDefined *TableAutoIncrement
	if strings.HasPrefix(versionDefined.Version, "8") {
		//mysql8.x中，table_stats中的数据不是实时更新的，但可以通过手动调用analyze table的方式让触发更新
		//之后才能通过table_stats获得自增主键当前的值
		//如果不使用analyze table的方式，也可以直接查`INFORMATION_SCHEMA`.`INNODB_TABLESTATS`中的信息，但是要带上数据库名
		//这里为了简化实现，使用了analyze table的方式
		Exec(db, TableAnalyze8_2(tableName))
		tableAutoincDefined = Query(tableAutoIncrementMeta, db, TableAutoInc8_2(tableName)).(*TableAutoIncrement)
	} else {
		tableAutoincDefined = Query(tableAutoIncrementMeta, db, TableAutoInc5(tableName)).(*TableAutoIncrement)
	}
	currAutoId = tableAutoincDefined.AutoIncrement

	//查询出当前表中最大的自增主键
	maxIdMeta := meta.NewMiniEntityMeta(&TableMaxId{})
	maxIdDefined := Query(maxIdMeta, db, SelectMaxId(primaryKey.ColumnName(), tableName)).(*TableMaxId)
	currMaxId := maxIdDefined.Maxid + 1

	maxId := currAutoId
	if currMaxId > maxId {
		maxId = currMaxId
	}
	//系统中自定义的ID规则
	rValue := entityMeta.NewEntityPtr().Interface()
	dbEntity := rValue.(orm.IDbEntity)
	customInitId := dbEntity.IdGenerator()
	var baseValue int64
	if customInitId > 0 {
		baseValue = customInitId + 1
	} else {
		baseValue = primaryKey.AutoBase()*SERVER_IDENTITY + 1
	}
	if baseValue > maxId {
		maxId = baseValue
	}
	if maxId > currAutoId {
		ddlSql := SetAutoIncrement(tableName, maxId)
		Exec(db, ddlSql)
		fmt.Printf("数据库DDL修改自增ID:%s,当前增加序列:%v,库中最大ID:%v,系统自定义ID:%v\n", ddlSql, currAutoId, currMaxId, baseValue)
	}
}
